#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/28 14:36
# @Author  : 王凯
# @File    : __init__.py.py
# @Project : scrapy_spider
import datetime
from collections import namedtuple
from typing import Optional, List, Union

import pandas as pd
from dateutil.relativedelta import relativedelta
from loguru import logger
from pydantic import BaseModel

IndustryNode = namedtuple("IndustryNode", ["name", "level", "fid"])


class IndustryModel(object):
    def __init__(self, industry_info):
        self.industry_info = industry_info
        self.index_maps = {}
        self.search_maps = {}
        self.category_maps = {"门类": "1", "大类": "2", "中类": "3", "子类": "4"}
        self.init()

    def init(self):
        for info in self.industry_info:
            index = str(info["number1"])
            self.index_maps[index] = IndustryNode(
                info["gjhyhf"], self.category_maps[info["category"]], info["father_category"]
            )
            self.search_maps[info["gjhyhf"]] = index

    def get(self, name):
        if name not in self.search_maps:
            return None
        index = self.search_maps.get(name)
        result = {}
        while index:
            node = self.index_maps[index]
            result.update({node.level: node.name})
            index = node.fid
        return result


IPCNode = namedtuple("IPCNode", ["name", "level", "father_code"])


class IPCModel(object):
    def __init__(self, ipc_info):
        self.ipc_info = ipc_info
        self.version = ipc_info[0].get("version")
        self.index_maps = {}
        self.search_maps = {}
        self.category_maps = {"一级": "-3", "二级": "-2", "三级": "-1", "四级": "0", "五级": "1", "六级": "2"}
        self.init()

    def init(self):
        for info in self.ipc_info:
            index = str(info["code"])
            self.index_maps[index] = IPCNode(info["name"], info["level"], info["father_code"])
            self.search_maps[str(info["name"])] = index

    def get(self, name):
        if name not in self.search_maps:
            return None
        index = self.search_maps.get(name)
        result = {}
        while index:
            node = self.index_maps[index]
            result.update({node.level: node.name})
            index = node.father_code
        return result

    def get_code(self, code):
        result = {}
        try:
            ret = self.index_maps[code]
            name = ret.name
            result = self.get(name=name)
            if result:
                result = dict(sorted(result.items(), key=lambda x: x[0]))
                result = [v for k, v in result.items()]
                result = {idx: v for idx, v in enumerate(result, start=1)}
        except Exception as e:
            # logger.error(f"{e} {self.version}")
            pass
        return result


LOCNode = namedtuple("LOCNode", ["name", "level", "father_code"])


class LOCModel(object):
    def __init__(self, loc_info):
        self.loc_info = loc_info
        self.index_maps = {}
        self.search_maps = {}
        self.init()

    def init(self):
        for info in self.loc_info:
            index = str(info["code"])
            self.index_maps[index] = LOCNode(
                info["name"], info["father_flag"], info["code"][:2] if len(info["code"]) > 2 else ""
            )
            self.search_maps[str(info["name"])] = index

    def get(self, name):
        if name not in self.search_maps:
            return None
        index = self.search_maps.get(name)
        result = {}
        while index:
            node = self.index_maps[index]
            result.update({node.level: node.name})
            index = node.father_code
        return result

    def get_code(self, code):
        result = {}
        try:
            ret = self.index_maps[code]
            name = ret.name
            result = self.get(name=name)
            if result:
                result = dict(sorted(result.items(), key=lambda x: x[0], reverse=True))
                result = [v for k, v in result.items()]
                result = {idx: v for idx, v in enumerate(result, start=1)}
        except Exception as e:
            # logger.error(e)
            pass
        return result


class LawStatusWash:
    def __init__(self, df_pro: pd.DataFrame):
        self.df_pro = df_pro

    @staticmethod
    def process_status_name(name: str) -> str:
        in_status = [
            "公布",
            "实质审查的生效",
            "实质审查请求",
            "审定",
            "实质审查请求的生效",
            # "专利局对专利申请实质审查的决定"   # todo
        ]
        effective_status = [
            "授权",
            "保密专利专利权授予",
            "专利申请或者专利权的恢复",
            "专利申请权、专利权的转移",
            "专利申请权、专利权的转移(专利权的转移)",
            "专利申请权、专利权的转移专利权的转移",
        ]
        passive_status = [
            '专利权的无效、部分无效宣告专利权全部无效', '专利权的终止(②专利权有效期届满)',
            '发明专利申请公布后的驳回', '专利权的无效宣告', '其他有关事项(避免重复授权放弃专利权)',
            '专利权的终止专利权有效期届满', '专利申请的视为撤回', '发明专利申请公布后的撤回',
            '专利权的无效宣告专利权全部无效', '专利权的终止(未缴年费专利权终止)',
            '专利权的终止(②未缴年费专利权终止)', '专利权的终止(专利权有效期届满)',
            '专利权的无效宣告(专利权全部无效)', '视为撤回的专利申请', '专利权的无效、部分无效宣告',
            '宣告专利权部分无效审查结论公告', '专利权的终止(①未缴年费专利权终止)',
            '专利权的终止未缴年费专利权终止', '其他有关事项避免重复授权放弃专利权', '专利申请的驳回',
            '其他有关事项专利权全部撤销',
            # '被视为撤回的申请',     # todo
            '专利权的无效、部分无效宣告(专利权全部无效)',
            '专利权的终止', '专利权的终止专利权的主动放弃', '避免重复授权放弃专利权',
            '发明专利申请公布后的视为撤回', '专利权的撤销(专利权全部撤销)', '专利权的视为放弃',
            '专利申请的撤回', '专利权的主动放弃'
        ]

        other_status = [
            '专利权的质押、保全及解除(专利权质押(保全)的解除)', '发明专利更正', '其他有关事项',
            '专利权人的姓名或者名称、地址的变更', '文件的公告送达',
            # '更正',   # todo
            '开放许可的声明及撤回',
            '专利权质押登记、变更及注销', '专利权质押合同登记的生效、变更及注销', '发明专利申请更正',
            # '专利实施的强制许可',    # todo
            # '其他事务不改变法律状态',  # todo
            '避免重复授予专利权', '权利的恢复', '变更',
            '地址不明的通知', '外观设计专利公报更正', '实用新型专利公报更正', '发明专利公报更正',
            # '其他有关事项专利权全部撤销', # todo
            '专利实施许可合同备案的生效、变更及注销', '被视为撤回的申请',
            '专利权人的姓名或者名称、国籍和地址的变更', '专利权的保全及其解除',
            # '保密专利的解密',  # todo
            '著录事项变更', '外观设计专利更正', '实用新型专利更正'
        ]

        if any(c == name for c in in_status):
            name = "审中"
        elif any(c == name for c in effective_status):
            name = "有效"
        elif any(c == name for c in passive_status):
            name = "无效"
        elif any(c == name for c in other_status):
            name = "其他事务不改变法律状态"
        # elif any(c in name for c in in_status):
        #     name = "审中"
        # elif any(c in name for c in effective_status):
        #     name = "有效"
        # elif any(c in name for c in passive_status):
        #     name = "无效"
        # elif any(c in name for c in other_status):
        #     name = "其他事务不改变法律状态"
        return name

    def process_status_dict_to_simple_law_status(self, status_list: list):
        status_list = status_list or []
        if status_list:
            status_list = sorted(status_list, key=lambda x: x['date'], reverse=True)
        ret = [
            {k: self.process_status_name(v) if k == "status" else v for k, v in status_dict.items()}
            for status_dict in status_list
        ]
        if ret:
            for i in ret:
                if i["status"] == "其他事务不改变法律状态":
                    continue
                return i["status"]
        return ''

    def process_status_dict_to_simple_law_status_fix(self, series):
        pub_date, grant_date, simple_law_status = series[["pub_date", "grant_date", "simple_law_status"]]
        if any(c in simple_law_status for c in ["其他事务不改变法律状态"]):
            if grant_date:
                simple_law_status = "有效"
            elif pub_date:
                simple_law_status = '审中'
        series["simple_law_status"] = simple_law_status
        return series

    @staticmethod
    def process_status_patent_progress(series):
        status_list, application_time = series[["law_status", "application_time"]]
        ret = status_list or []
        if application_time:
            ret.append({"date": application_time, "detail": "申请", "status": "申请"})
            ret.append({
                "date": (datetime.datetime.strptime(application_time, "%Y-%m-%d") + relativedelta(years=20)).strftime("%Y-%m-%d"),
                "detail": "预估到期", "status": "预估到期"
            })

        result = []
        for i in ret:
            if (i.get("status") or "") in ["申请", "公布", "授权", '终止', '预估到期']:
                result.append({"date": i.get("date"), "detail": i.get("status")})
            elif (i.get("detail") or "") in ["申请", "公布", "授权", '终止', '预估到期']:
                result.append({"date": i.get("date"), "detail": i.get("status")})
        return result

    def run(self) -> pd.DataFrame:
        if "law_status" not in self.df_pro.columns:
            return self.df_pro
        self.df_pro["simple_law_status"] = self.df_pro["law_status"].map(
            lambda x: self.process_status_dict_to_simple_law_status(x)
        )
        self.df_pro = self.df_pro.apply(self.process_status_dict_to_simple_law_status_fix, axis=1)
        self.df_pro["patent_progress"] = self.df_pro.apply(self.process_status_patent_progress, axis=1)
        return self.df_pro


class LawStatus(BaseModel):
    date: Optional[str]
    status: Optional[str]
    detail: Optional[str]


class LawProgress(BaseModel):
    date: Optional[str]
    detail: Optional[str]


class PatentOutInfo(BaseModel):
    company_name: Optional[str]
    applicant_name: List[str]
    taxpayer_id: Optional[str]
    taxpayer_industry1: Optional[str]
    taxpayer_industry2: Optional[str]
    taxpayer_industry3: Optional[str]
    taxpayer_industry4: Optional[str]
    address: Optional[str]
    post_code: Optional[str]

    inventor: List[str]
    patent_name: Optional[str]
    patent_type: Optional[str]
    patent_progress: Optional[List[LawProgress]]
    simple_law_status: Optional[str]

    pub_number: Optional[str]
    pub_date: Optional[str]
    grant_number: Optional[str]
    grant_date: Optional[str]
    patent_num: Optional[str]
    app_number: Optional[str]
    application_time: Optional[str]

    all_cat_num: Optional[List[str]]
    main_cat_num: Optional[str]
    patent_category1: Optional[str]
    patent_category2: Optional[str]
    patent_category3: Optional[str]
    patent_category4: Optional[str]
    law_status: Optional[List[LawStatus]]

    agent: Optional[str]
    agency: Optional[str]
    abstracts: Optional[Union[str, dict]]
